---
title: useQueryStates
description: How to read & update multiple search params at once
---

## Multiple updates (batching)

You can call as many state update functions as needed in a single event loop
tick, and they will be applied to the URL asynchronously:

```ts
const MultipleQueriesDemo = () => {
  const [lat, setLat] = useQueryState('lat', parseAsFloat)
  const [lng, setLng] = useQueryState('lng', parseAsFloat)
  const randomCoordinates = React.useCallback(() => {
    setLat(Math.random() * 180 - 90)
    setLng(Math.random() * 360 - 180)
  }, [])
}
```

If you wish to know when the URL has been updated, and what it contains, you can
await the Promise returned by the state updater function, which gives you the
updated URLSearchParameters object:

```ts
const randomCoordinates = React.useCallback(() => {
  setLat(42)
  return setLng(12)
}, [])

randomCoordinates().then((search: URLSearchParams) => {
  search.get('lat') // 42
  search.get('lng') // 12, has been queued and batch-updated
})
```

<details>
<summary><em>Implementation details (Promise caching)</em></summary>

The returned Promise is cached until the next flush to the URL occurs,
so all calls to a setState (of any hook) in the same event loop tick will
return the same Promise reference.

Due to throttling of calls to the Web History API, the Promise may be cached
for several ticks. Batched updates will be merged and flushed once to the URL.
This means not every setState will reflect to the URL, if another one comes
overriding it before flush occurs.

The returned React state will reflect all set values instantly,
to keep UI responsive.

---

</details>

## `useQueryStates`

For query keys that should always move together, you can use `useQueryStates`
with an object containing each key's type:

```ts
import { useQueryStates, parseAsFloat } from 'nuqs'

const [coordinates, setCoordinates] = useQueryStates(
  {
    lat: parseAsFloat.withDefault(45.18),
    lng: parseAsFloat.withDefault(5.72)
  },
  {
    history: 'push'
  }
)

const { lat, lng } = coordinates

// Set all (or a subset of) the keys in one go:
const search = await setCoordinates({
  lat: Math.random() * 180 - 90,
  lng: Math.random() * 360 - 180
})
```

### Options

There are three places you can define [options](/docs/options) in `useQueryStates`:
- As the second argument to the hook itself (global options, like `history: 'push'{:ts}` above)
- On each parser, like `parseAsFloat.withOptions({ shallow: false }){:ts}`
- At the call level when updating the state:

```ts
setCoordinates(
  {
    lat: 42,
    lng: 12
  },
  // [!code highlight:3]
  {
    shallow: false
  }
)
```

The order of precedence is: call-level options > parser options > global options.

<Callout title="Tip">
You can clear all keys managed by a `useQueryStates{:ts}` hook by passing
`null{:ts}` to the state updater function:

```ts
const clearAll = () => setCoordinates(null)
```

This will clear `lat` & `lng`, and leave other search params untouched.

</Callout>

### Shorter search params keys

<FeatureSupportMatrix
  introducedInVersion='1.20.0'
  support={{
    supported: false,
    frameworks: ['TanStack Router']
  }}
  highlightUnsupported
/>

One issue with tying the parsers object keys to the search params keys was that
you had to trade-off between variable names that make sense for your domain
or business logic, and short, URL-friendly keys.

You can use a `urlKeys{:ts}` object in the hook options
to remap the variable names to shorter keys:

```ts
const [{ latitude, longitude }, setCoordinates] = useQueryStates(
  {
    // Use variable names that make sense in your codebase
    latitude: parseAsFloat.withDefault(45.18),
    longitude: parseAsFloat.withDefault(5.72)
  },
  {
    urlKeys: {
      // And remap them to shorter keys in the URL
      latitude: 'lat',
      longitude: 'lng'
    }
  }
)

// No changes in the setter API, but the keys are remapped to:
// ?lat=45.18&lng=5.72
setCoordinates({
  latitude: 45.18,
  longitude: 5.72
})
```

As your application grows, you may want to reuse these parsers and urlKeys
definitions across multiple components or nuqs features
(like [loaders](/docs/server-side#loaders) or a [serializer](/docs/utilities#serializer-helper)).

You can use the `UrlKeys{:ts}` type helper for this:

```ts
// [!code word:UrlKeys]
import { type UrlKeys } from 'nuqs' // or 'nuqs/server'

export const coordinatesParsers = {
  latitude: parseAsFloat.withDefault(45.18),
  longitude: parseAsFloat.withDefault(5.72)
}

export const coordinatesUrlKeys: UrlKeys<typeof coordinatesParsers> = {
  latitude: 'lat',
  longitude: 'lng'
}
```

<FeatureSupportMatrix introducedInVersion='2.3.0' hideFrameworks />